<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
    提交链接： https://jinshuju.net/f/G42Yv8
    <div id="app"></div>
    <script>
        window.nodes = new Map()
        class Vnode {
            constructor(tag, attr, children, parent, childrenTemplate) {
                this.tag = tag;
                this.attr = attr;
                this.children = children;
                this.parent = parent;
                this.childrenTemplate = childrenTemplate;
                this.uuid = this.uuid();
            }

            uuid() {
                return (
                    Math.random() * 10000000000 +
                    Math.random() * 100000 +
                    Date.now()
                ).toString(36);
            }
        }
        function render(template, data) {
            const re1 = /<(\w+)\s*([^>]*)>([^<]*)<\/\1>/gm; //匹配有子节点的标签<div class="a">XXX</div>
            const re2 = /<(\w+)\s*([^(/>)]*)\/>/gm; //匹配闭合标签，如<input type="text"/> ，<img src="a"/>
            template = template.replace(/\n/gm, "");
            while (re1.test(template) || re2.test(template)) {
                //<div class="a">XXX</div>类型
                template = template.replace(re1, (s0, s1, s2, s3) => {
                    let attr = parseAttribute(s2);
                    // let children = parseAttribute(s3);
                    let node = new Vnode(s1, attr, [], null, s3);
                    this.nodes.set(node.uuid, node);
                    return `(${node.uuid})`;
                });
                //<img src="a"/>类型
                template = template.replace(re2, (s0, s1, s2) => {
                    let attr = parseAttribute(s2);
                    let node = new Vnode(s1, attr, [], null, "");
                    this.nodes.set(node.uuid, node);
                    return `(${node.uuid})`;
                });
            }
            console.log("第一阶段|解析创建node>>>", this.nodes);
            let rootNode = parseToNode(template);
            console.log("第二阶段|构建nodeTree>>>", rootNode);
            let dom = parseNodeToDom(rootNode, data);
            console.log("第三阶段|nodeTree To DomTree>>>", dom);
            return dom;
        }
        function parseAttribute(str) {
            let attr = new Map();
            str = str.trim();
            str.replace(/(\w+)\s*=['"](.*?)['"]/gm, (s0, s1, s2) => {
                attr.set(s1, s2);
                return s0;
            });
            return attr;
        }
        function parseToNode(template) {
            let re = /\((.*?)\)/g;
            let stack = [];
            let parent = new Vnode("root", {}, [], null, template, null);
            stack.push(parent);
            //转成成node节点
            while (stack.length > 0) {
                let pnode = stack.pop();
                let nodestr = pnode.childrenTemplate.trim();
                re.lastIndex = 0;
                [...nodestr.matchAll(re)].forEach((item) => {
                    let n = this.nodes.get(item[1]);
                    let newn = new Vnode(
                        n.tag,
                        n.attr,
                        [],
                        pnode,
                        n.childrenTemplate,
                        null
                    );
                    pnode.children.push(newn);
                    stack.push(newn);
                });
            }
            return parent.children[0];
        }
        function parseNodeToDom(root, data) {
            let fragment = document.createDocumentFragment();
            let stack = [[root, fragment, data]];
            //转成成node节点
            while (stack.length > 0) {
                let [pnode, pdom, scope] = stack.shift();
                if (pnode.attr.get("for")) {
                    let [key, prop] = pnode.attr.get("for").split("in");
                    key = key.trim();
                    prop = prop.trim();
                    for (let i = 0; i < scope[prop].length; i++) {
                        let newnode = new Vnode(
                            pnode.tag,
                            pnode.attr,
                            pnode.children,
                            pnode.parent,
                            pnode.childrenTemplate
                        );
                        let newScope = {};
                        newScope[key] = scope[prop][i];
                        let html = scopehtmlParse(newnode, data, newScope);
                        let ele = createElement(newnode, html);
                        scopeAtrrParse(ele, newnode, data, newScope);
                        pdom.parentNode.appendChild(ele);
                        newnode.children.forEach((item) => {
                            stack.push([item, ele, newScope]);
                        });
                    }
                } else if (pnode.attr.get("if")){
                    let [prop, propProperties] = pnode.attr.get("if").split(".");
                    pnode.attr.delete("if");
                    if (prop && scope[prop]) {
                        if (propProperties && !scope[prop][propProperties]) {
                            console.log(`属性对象的属性${propProperties}==false`)
                            continue
                        }
                        let html = scopehtmlParse(pnode, data, scope);
                        let ele = createElement(pnode, html);
                        scopeAtrrParse(ele, pnode, data, scope);
                        pdom.appendChild(ele);

                        pnode.children.forEach((item) => {
                            stack.push([item, ele, scope]);
                        });
                    }                    
                } else {
                    let html = scopehtmlParse(pnode, data, scope);
                    let ele = createElement(pnode, html);
                    scopeAtrrParse(ele, pnode, data, scope);
                    pdom.appendChild(ele);

                    pnode.children.forEach((item) => {
                        stack.push([item, ele, scope]);
                    });
                }
            }
            return fragment;
        }
        function scopehtmlParse(node, globalScope, curentScope) {
            return node.childrenTemplate.replace(/\{\{(.*?)\}\}/g, (s0, s1) => {
                let props = s1.split(".");
                let val = curentScope[props[0]] || globalScope[props[0]];
                props.slice(1).forEach((item) => {
                    val = val[item];
                });
                return val;
            });
        }

        function createElement(node, html) {
            let ignoreAttr = ["for", "click"];
            let dom = document.createElement(node.tag);
            for (let [key, val] of node.attr) {
                if (!ignoreAttr.includes(key)) {
                    dom.setAttribute(key, val);
                }
            }
            if (node.children.length === 0) {
                dom.innerHTML = html;
            }
            return dom;
        }
        function scopeAtrrParse(ele, node, globalScope, curentScope) {
            console.log(node.attr);
            for (let [key, value] of node.attr) {
                let result = /\{\{(.*?)\}\}/.exec(value);
                if (result && result.length > 0) {
                    let props = result[1].split(".");
                    let val = curentScope[props[0]] || globalScope[props[0]];
                    props.slice(1).forEach((item) => {
                        val = val[item];
                    });
                    ele.setAttribute(key, val);
                }
            }
        }

        let tmpl = `<div class="newslist">
            <div class="img1" v-if="info.showImage"><img src="{{image}}"/></div>
            <div class="date" v-if="info.showDate">{{info.name}}</div>
            <div class="img2">{{info.name}}</div>
            <ul v-if="list.length">
                <li v-for="item in list">{{item}}</li>
            </ul>
        </div>`;
        // tmpl = `<div>
        //     <ul v-if="list.length">
        //         <li v-for="item in list">{{item}}</li>
        //     </ul>
        // </div>`;
        // tmpl = `<div class="img1" v-if="info.showImage"><img src="{{image}}"/></div>`;

        let result = render(tmpl, {
            image: "https://fanyi-cdn.cdn.bcebos.com/static/translation/img/header/logo_e835568.png",
            info: { showImage: true, showDate: false, name: "aaa" },
            list: ['li1', 'li2', 'li3']
        })
        console.log(result)
        document.getElementById('app').appendChild(result.firstElementChild)
    </script>
</body>

</html>